using System;
using System.Collections.Generic;
using System.Text;
using Noein.GeneticAlgorithm.Final;

namespace Noein.GeneticAlgoirthm.Final
{
    //generate bits of lisp
    //eventually: generate random lisp programs of specifed length
    public class LispGen
    {
        Random rand;
        //replace this to whatever the damned mutation rate is.
        public const float mRate = (float).02;
        //an etherial constant
        public const float vLikelihood = (float).5; //well, I wish

        //# of funcs
        public const int fCount = 4;

        public LispGen()
        {
            rand = new Random();

            //initialize valid functions
            funcS = new string[fCount];
            //I know that there's a better way to do it, but it takes time, which is precious
            //to me.
            funcS[0] = "+";
            funcS[1] = "-";
            funcS[2] = "/";
            funcS[3] = "*";

        }
        enum CodeBits
        {
            statement,
            func,
            arg

        }//enum CodeBits

        //Errr, right now I'm not sure what this enum is for?
        enum Func
        {
            add,
            sub,
            div,
            mult
        }
        string[] funcS;

        enum Arg
        {
            lString
        }

        //return a random program with parse tree of given width and depth
        //not implemented: randomness
        public GenoPheno randProgram(int width, int depth)
        {
            GenoPheno gp = new GenoPheno(getFunc(), 0, false);
            //right child is an argument for now
            gp.setRight(new GenoPheno(getArg(), 1, true));
            //start replacing left children till we is long enough
            int d = gp.getDepth() - 1;
            GenoPheno current = gp;
            GenoPheno next;

            //deepen
            while (d < depth)
            {
                //set the left child to a new valid GenoPheno with a valid argument as the right leaf
                next = new GenoPheno(getFunc(), 0, true);
                //next's right leaf
                next.setRight(new GenoPheno(getArg(), 1, true));
                current.setLeft(next);
                current = next;


                d++; //:D
            }
            //whatever the last one was, make it's left leaf a valid argument
            current.setLeft(new GenoPheno(getArg(), 1, true));

            //I need a separate function for widen because it is recursive and could get hairy
            //forget width for now-that will just be modified in mutation and crossover

            //widen(gp, width);
            return gp;
        }
        //spread this program out
        /*  private void widen(GenoPheno gp, int width)
          {
              //if this is a leaf, replace it with a function
              if (gp.getType() == 1)//"temporary" hack onw hether this is a leaf or function
              {
                  gp = new GenoPheno(getFunc(), 0, false);
                  gp.setLeft(new GenoPheno(getArg(), 1, true));
                  gp.setRight(new GenoPheno(getArg(), 1, true));
                  //check and repeat
              }
              //if it is an argument, widen it's leaves
              else
              {
                  widen(gp.getLeft(), width - gp.getWidth());
                  widen(gp.getRight(), width - gp.getWidth());
              }

          }//widen
  */





        //generate a random valid lisp statement from among the list of lisp statements
        public string getStatement()
        {
            return "(" + getFunc() + " " + " " + getArg() + " " + getArg() + ")";
        }

        //return a random valid lisp function from among a subset of lisp's functions
        public string getFunc()
        {
            int i = rand.Next(0, fCount);

            //know what I'm sayin'
            if (i >= fCount)
                throw new Exception(" LispGen.getFunc()");

            //Console.WriteLine(i);
            return funcS[i];

        }

        //return a random valid lisp argument
        //we'll make it a random float between -1 and 1
        public string getArg()
        {
            double negRate = rand.NextDouble();
            double varRoll = rand.NextDouble();
            double f = (float)rand.NextDouble();
            //need to decouple half being negative from some being variables
            //half of the values are negated
            if (negRate < .5)//i.e. within the mutation rate
                f *= -1;
            if (vLikelihood < varRoll)//so variables appear proportionately to the mutation rate o_o(?)
            {
                //Console.WriteLine(Convert.ToString(f));
                return Convert.ToString(f);
            }
            else //if and only if (vLikelihood >B= varRoll)
            {
                String s = Convert.ToString("V");
                //Console.WriteLine(s);
                return s;
            }
        }



    }//class LispGen
}//namespace ConsoleApplication1


//add functionality for:
/*
returning random valid functions

returning random valid arguments

returning random valid statements
*/



/* A bunch of code that didn'try work, and so I'm just gonna put iterator here.  Damn iterator, stop auto-completing.

            //get a statement, make it our root node
            GenoPheno gp = new GenoPheno(getFunc(), 0, false); //hacking the "type" because I know 0 is for functions and 1 is for arguments
            //set its right child to something, because it needs at least 2 (for now)
            gp.setRight(new GenoPheno(getArg(), 1, true));

            //First, mutate it out to depth by just replacing each left child until
            //we've got a deep enough program
            int x = 0;
            GenoPheno current = gp;
            /*while (gp.getDepth() < depth)
            {
                current.setLeft(new GenoPheno(getFunc(), 0, false ));
                current.setRight(new GenoPheno(getArg(), 1, true));
                current = current.getLeft();
                x++;
            }
            //for this, we'll need GenoPhenos
 */